package jsp.servlet;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileInputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jpa.entities.Project;
import jpa.entities.Revision;
import jsp.classes.Session;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.CharacterRun;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;

/**
 * @author Remco Haszing
 * @author Robin Fokker
 * @author Jorik Heijstek
 */
public class ConvertToPdf extends HttpServlet {

    /**
     * Download files as a pdf. This method merges multiple files of type doc,
     * docx, xls, xlsx, png, jpg or gif into a single pdf file and sends it to
     * the response.
     *
     * @param request The request to handle.
     * @param response The response to send the file to.
     */
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) {
        Session session = (Session) request.getSession(true).getAttribute("user");
        Project project = null;
        String[] revisions = null;
        for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
            String k = entry.getKey();
            String[] v = entry.getValue();
            if (k.equals("p")) {
                project = session.findProjectById(Long.parseLong(v[0]));
            } else if (k.equals("r")) {
                revisions = v;
            }
        }
        try {
            ServletOutputStream stream = response.getOutputStream();
            Document document = new Document();
            PdfWriter writer = PdfWriter.getInstance(document, stream);
            document.open();
            for (String s : revisions) {
                Revision r = project.getRootFolder().findRevisionById(Long.parseLong(s));
                String t = r.getContentType();
                String n = session.getUploadDir() + "/" + project.getId() + "/" + r.getItem().getId() + "/" + r.getVersion();
                if (t.equals("application/msword")) {
                    document.add(readDoc(n));
                } else if (t.equals("application/vnd.openxmlformats-officedocument.wordprocessingml.document")) {
                    document.add(readDocx(n));
                } else if (t.equals("application/vnd.ms-excel") || t.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) {
                    document.add(readXls(n));
                } else if (t.startsWith("image")) {
                    document.add(Image.getInstance(n));
                }
            }
            document.close();
            stream.flush();
            stream.close();
            writer.close();
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Finds a matching color. This method returns a basecolor matching a
     *
     * @param color
     * @return
     */
    private static BaseColor getFontColor(int color) {
        switch (color) {
            case 1:
                return BaseColor.BLACK;
            case 2:
                return new BaseColor(141, 179, 226);
            case 3:
                return BaseColor.CYAN;
            case 5:
                return BaseColor.MAGENTA;
            case 6:
                return BaseColor.RED;
            case 7:
                return BaseColor.YELLOW;
            case 8:
                return BaseColor.WHITE;
            case 9:
                return new BaseColor(0, 32, 96);
            case 10:
                return BaseColor.BLUE;
            case 11:
                return BaseColor.GREEN;
            case 13:
                return new BaseColor(152, 72, 6);
            case 14:
                return new BaseColor(79, 98, 40);
            case 15:
                return BaseColor.DARK_GRAY;
            case 16:
                return BaseColor.LIGHT_GRAY;
        }
        return BaseColor.BLACK;
    }

    /**
     * Creates a font with the requested parameters. This method creates and
     * returns a font with the given parameters.
     *
     * @param family The font family.
     * @param size The font size.
     * @param color The color of the font.
     * @param style The font style. eg "normal bold italic oblique underline
     * line-through".
     * @return The created font.
     */
    private static Font createFont(String family, float size, BaseColor color, String style) {
        Font f = new Font();
        f.setFamily(family);
        f.setSize(size);
        f.setColor(color);
        f.setStyle(style);
        return f;
    }

    private static Phrase readDoc(String docFile) throws Exception {
        Phrase phrase = new Phrase();
        Range range = new HWPFDocument(new POIFSFileSystem(new FileInputStream(docFile))).getRange();
        for (int i = 0; i < range.numCharacterRuns(); i++) {
            CharacterRun r = range.getCharacterRun(i);
            String style = "";
            if (r.isBold()) {
                style += " bold";
            }
            if (r.isItalic()) {
                style += " italic";
            }
            if (r.getUnderlineCode() > 0) {
                style += " underline";
            }
            if (r.isStrikeThrough()) {
                style += " line-through";
            }
            String text = r.text();
            Chunk c;
            if (text.contains("HYPERLINK")) {
                i++;
                c = new Chunk(range.getCharacterRun(i).text()).setAnchor(text.split("\"")[1]);
            } else {
                c = new Chunk(text);
            }
            c.setFont(createFont(r.getFontName(), (float) r.getFontSize() / 3, getFontColor(r.getColor()), style));
            c.setCharacterSpacing(r.getCharacterSpacing());
            phrase.add(c);
        }
        phrase.add(new Chunk("\n"));
        return phrase;
    }

    private static Phrase readDocx(String docxFile) throws Exception {
        Phrase phrase = new Phrase();
        List<XWPFParagraph> list = new XWPFDocument(new FileInputStream(docxFile)).getParagraphs();
        for (XWPFParagraph p : list) {
            List<XWPFRun> runs = p.getRuns();
            int i = 0;
            for (XWPFRun r : runs) {
                i++;
                String text = r.getText(0);
                if (text != null) {
                    String color = r.getColor();
                    String style = "";
                    if (r.isBold()) {
                        style += " bold";
                    }
                    if (r.isItalic()) {
                        style += " italic";
                    }
                    if (r.getUnderline().getValue() != 18) {
                        style += " underline";
                    }
                    if (r.isStrike()) {
                        style += " line-through";
                    }
                    String font = r.getFontFamily();
                    Chunk c = new Chunk(text);
                    c.setFont(createFont(font == null ? "Arial" : font, r.getFontSize(), new BaseColor(Integer.parseInt(color.substring(0, 2), 16), Integer.parseInt(color.substring(2, 4), 16), Integer.parseInt(color.substring(4, 6), 16)), style));
                    phrase.add(c);
                }
                if (i == runs.size()) {
                    phrase.add(new Chunk("\n"));
                }
            }
        }
        return phrase;
    }

    private static Phrase readXls(String xlsFile) throws Exception {
        Phrase phrase = new Phrase();
        Workbook workbook = xlsFile.endsWith("x") ? new XSSFWorkbook(new FileInputStream(xlsFile)) : new HSSFWorkbook(new POIFSFileSystem(new FileInputStream(xlsFile)));
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            Sheet sheet = workbook.getSheetAt(i);
            if (sheet.getLastRowNum() != 0) {
                short width = 0;
                Iterator<Row> rows = sheet.rowIterator();
                while (rows.hasNext()) {
                    short w = rows.next().getLastCellNum();
                    if (width < w) {
                        width = w;
                    }
                }
                rows = sheet.rowIterator();
                PdfPTable table = new PdfPTable(width);
                while (rows.hasNext()) {
                    Iterator<Cell> cells = rows.next().cellIterator();
                    for (int j = 0; j < width; j++) {
                        if (cells.hasNext()) {
                            Cell cell = cells.next();
                            PdfPCell c = new PdfPCell(new Phrase(cell.toString()));
                            table.addCell(c);
                        } else {
                            table.addCell(new PdfPCell());
                        }
                    }
                }
                phrase.add(table);
                phrase.add(new Chunk("\n"));
            }
        }
        return phrase;
    }
}
